import java.util.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import javax.swing.JOptionPane;

/**
 * SundialMath - calculate the degree for Hour Lines user can get the degree of
 * gnomon using getGnomon() get the degree for hour lines using getHourLines()
 * Note:	This version only calculate the degree of Hour Lines every 30 mins
 *
 * @author Arun Lee, Michael Terada, Marek Lopez
 */
public class SundialMath {

    private double lat;
    private double lon;
    private String date;
    private double[] HL = new double[25];

    /**
     * SundialMath create an object of SundialMath
     */
    public SundialMath() {
        super();
    }

    /**
     * Main - basically just test calls
     *
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        SundialMath test = new SundialMath();
        test.setInputs();
        test.printHL();
    }

    /**
     * setInputs - get inputs from Scanner
     */
    public void setInputs() {
        HL = new double[25];
        Scanner one = new Scanner(System.in);
        Scanner two = new Scanner(System.in);

        System.out.print("Please enter the latitue: ");
        lat = one.nextDouble();
        System.out.print("\nPlease enter the longitude: ");
        lon = one.nextDouble();
        System.out.print("\nPlease enter the date: ");
        date = two.nextLine();

        if (lat > 90 || lat < -90) {
            JOptionPane.showMessageDialog(null, "Latitude's value shuold be in the range of -90 to 90");
            lat = 0;
            lon = 0;
        } else if (lon > 180 || lon < -180) {
            JOptionPane.showMessageDialog(null, "Longitude's value should be in the range of -180 to 180");
            lat = 0;
            lon = 0;
        } else {
            setHourLines(lat, lon);
        }
    }

    /**
     * setInputs - for other program to send inputs to SundialMath
     *
     * @param a double - latitude
     * @param b double - longitude
     * @param c String - date
     */
    public void setInputs(double a, double b, String c) {
        HL = new double[25];
        lat = a;
        lon = b;
        date = c;

        if (lat > 90 || lat < -90) {
            JOptionPane.showMessageDialog(null, "Latitude's value shuold be in the range of -90 to 90");
            lat = 0;
            lon = 0;
        } else if (lon > 180 || lon < -180) {
            JOptionPane.showMessageDialog(null, "Longitude's value should be in the range of -180 to 180");
            lat = 0;
            lon = 0;
        } else {
            setHourLines(lat, lon);
        }
    }

    /**
     * setInitialHL - get the degrees for Hour Lines on the dial face of a
     * sundial Right now it is just the initial algorithm
     *
     * @param latitude double - the latitude
     */
    public void setHourLines(double latitude, double longitude) {
        BigDecimal temp;
        double sMeridian = 0;
        double sunrise = 6;
        Timezone tz = new Timezone(Double.toString(latitude), Double.toString(longitude));

        sMeridian = tz.getRawOffset();
        sMeridian = sMeridian / 3600 * 15;

        if (longitude > sMeridian) {	//meaning the location is east of the Standard Meridian
            sunrise = (sunrise + ((longitude - sMeridian) * 4) / 60);
        } else {	//meaning the location is west of the Standard Meridian
            sunrise = (sunrise - ((sMeridian - longitude) * 4) / 60);
        }

        //apply equation of time correction
        sunrise = sunrise - EOTCorrection();

        for (int i = 0; i < HL.length; i++) {
            HL[i] = Math.toDegrees(Math.atan(Math.sin(Math.toRadians(Math.abs(latitude))) * (Math.tan(Math.toRadians((sunrise - 12) * 15)))));
            temp = BigDecimal.valueOf(HL[i]);
            temp = temp.setScale(3, RoundingMode.HALF_UP);
            HL[i] = temp.doubleValue();
            sunrise = sunrise + 0.5;
        }
    }

    /**
     * EOTCorrection - calculate the equation of time using information from
     * http://www.susdesign.com/popups/sunangle/eot.php
     *
     * @return double - the correction in hour
     */
    private double EOTCorrection() {
        double correction = 0;

        double B = 360 * ((double) getDayNumber() - 81) / 365;
        if (B != 360 * ((0.0 - 81) / 365)) {
            correction = 9.87 * Math.sin(Math.toRadians(2 * B)) - 7.53 * Math.cos(Math.toRadians(B)) - 1.5 * Math.sin(Math.toRadians(B));
        }
        return correction / 60;
    }

    /**
     * getDayNumber - use the date collected from getInputs to calculate the day
     * number
     *
     * @return int - the day number
     */
    private int getDayNumber() {
        //variables
        int daynum = 0;
        int year = 0;
        int day = 0;
        int month = 0;
        //number of days in each month
        int[] daysofmonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        //split the date string into year, month, and day
        String[] temp = date.split("/");
        if (temp.length == 3) {	//check if there are three parts
            year = Integer.parseInt(temp[0]);
            month = Integer.parseInt(temp[1]);
            day = Integer.parseInt(temp[2]);
            //check if month and day is valid
            if (month >= 1 && month <= 12 && day <= daysofmonth[month - 1]) {
                if (month == 1) {
                    daynum = day;
                } else {
                    for (int i = 0; i < month - 1; i++) {
                        daynum = daynum + daysofmonth[i];
                    }
                    daynum = daynum + day;
                }
            } else {
                JOptionPane.showMessageDialog(null, "The month or day is invalid. \nA Sundial without equation of time correction will be created.");
            }
        } else {
            JOptionPane.showMessageDialog(null, "Format error, please re-enter the date. \nA Sundial without equation of time correction will be created.");
        }
        return daynum;
    }

    /**
     * printHL - print out all the hour line's degree
     */
    public void printHL() {
        double sunrise = 6;
        System.out.println("Gnomon/latitude: " + lat);
        System.out.println("longitude: " + lon);
        System.out.println("date: " + date);
        for (int j = 0; j < HL.length; j++) {
            System.out.println("The angle at " + sunrise + " " + HL[j]);
            sunrise = sunrise + 0.5;
        }
    }

    /**
     * getGnomon - for other program to get the degree of gnomon
     *
     * @return double lat - since the degree of gnomon is the same as the
     * latitude we return lat
     */
    public double getGnomon() {
        return Math.abs(lat);
    }

    /**
     * getHourLines - for other program to get the hour line's degree
     *
     * @return double[] HL - all the degree from 6am to 6pm are stored in HL
     */
    public double[] getHourLines() {
        return HL;
    }
}
